﻿using System;
using System.Drawing;
using System.Linq;
using MoreLinq;
using Redmine.Laptop.API;
using Redmine.Laptop.Common.Core;
using Redmine.Laptop.Common.Core.Enums;
using Redmine.Laptop.Common.Extensions;
using Redmine.Laptop.Reports.ReportParameters;

namespace Redmine.Laptop.Reports.Reports
{
    public class TimesheetReport : Report
    {
        #region Members
        private readonly TimesheetReportParameters _parameters;
       
        private const uint topRow = 2;
        private const uint leftColumn = 2;
        private uint bottomRow; 
        private uint rightColumn;

        #endregion

        #region Properties
       
        #endregion

        public TimesheetReport(RedmineService redmineService, ReportType type, Parameters parameters)
            : base(redmineService, type, parameters)
        {
            _parameters = (TimesheetReportParameters)parameters;
        }

        public override string ReportName
        {
            get
            {
                return string.Format("Timesheet report ({0}-{1})", _parameters.FromDate.ToString("dd.MM.yyyy"), _parameters.ThruDate.ToString("dd.MM.yyyy"));
            }
        }

        protected override void CreateReportStructure()
        {
            DrawTableLayout();
            FillProjects();

            var datePeriod = new DatePeriod(_parameters.FromDate, _parameters.ThruDate);
            FillDeveloperHours(datePeriod);
        }

        private void DrawTableLayout()
        {
            var projectsCount = ProjectsInfo.Count();
            var developersCount = TimeEntries.Select(te => te.User.UserName).Distinct().Count();
            
            bottomRow = (uint)(topRow + projectsCount + 5);
            rightColumn = (uint)(leftColumn + developersCount + 1);

            // draw internal table borders
            Document.SetBorder(topRow, leftColumn, bottomRow, rightColumn, Color.Black, BorderWeight.Hairline, LineStyle.Dot);

            // draw line between developers
            for (var i = leftColumn; i <= rightColumn; i++)
                Document.SetBorder(topRow, i, bottomRow, i, Color.Black, isDrawAllBorders: false, isDrawRightEdge: true);

            // set styles for projects column 
            Document.SetBorder(topRow, leftColumn, bottomRow, leftColumn, Color.Black);
            Document.SetBoldFont(topRow, leftColumn, bottomRow, leftColumn);

            // fill footer labels, draw footer background
            Document[bottomRow - 4, leftColumn] = @"Итого рабочих часов: ";
            Document[bottomRow - 3, leftColumn] = @"Итого отработанных часов: ";
            Document[bottomRow - 2, leftColumn] = @"Итого коммерческих часов: ";
            Document[bottomRow - 1, leftColumn] = @" % Коммерческих часов от отработанных: ";
            Document[bottomRow, leftColumn] = @" % Коммерческих часов от рабочих: ";

            Document.SetBorder((uint)(topRow + projectsCount + 1), leftColumn, (uint)(topRow + projectsCount + 5), rightColumn, Color.Black);
            Document.SetBackground(CustomColor.LightGreen, (uint)(topRow + projectsCount + 1), leftColumn, (uint)(topRow + projectsCount + 3), rightColumn - 1);
            Document.SetBackground(Color.Red, (uint)(topRow + projectsCount + 1), rightColumn, (uint)(topRow + projectsCount + 5), rightColumn);
            Document.SetBackground(CustomColor.DarkPink, (uint)(topRow + projectsCount + 4), leftColumn, (uint)(topRow + projectsCount + 5), rightColumn - 1);
            
            // draw header
            Document[topRow, leftColumn] = @"Проект \ Разработчик";
            Document[topRow, rightColumn] = @"Итого: ";

            Document.SetBoldFont(topRow, leftColumn, topRow, rightColumn);
            Document.SetBackground(CustomColor.LightGreen, topRow, leftColumn, topRow, rightColumn);
            Document.SetBorder(topRow, leftColumn, topRow, rightColumn, Color.Black);
            Document.SetFontSize(14, topRow, leftColumn, topRow, leftColumn);
            

            // fill summary column
            Document.SetBorder(topRow, rightColumn, bottomRow, rightColumn, Color.Black);
            Document.SetBackground(CustomColor.LightGreen, topRow, rightColumn, (uint)(topRow + projectsCount), rightColumn);
            
            // fill background for non commercial projects
            foreach (var project in ProjectsInfo.Where(p => p.IsNonCommercial))
                Document.SetBackground(CustomColor.LightGray, (uint)(topRow + project.Index + 1), leftColumn, (uint)(topRow + project.Index + 1), rightColumn);
        }

        private void FillProjects()
        {
            var projects = 
                TimeEntries.Select(te => te.Project.ProjectName)
                .Distinct()
                .OrderBy(p => p)
                .Select(p => new {ProjectName = p, IsNonCommercial = NonCommercialProjectsNames.Contains(p)});
            
            // fill projects
            uint rowIndex = 3;
            foreach (var project in projects)
                Document[rowIndex++, 2] = project.ProjectName;
        }

        private void FillDeveloperHours(DatePeriod period)
        {
            var workingDaysCount = period.Count(d => !d.IsWeekend());

            var developerProjectHours =
                from te in TimeEntries
                group te by new {te.User.UserName, te.Project.ProjectName} into grouped
                let names = grouped.Key.UserName.Split(' ')
                let workingHoursPerWeek = GetWorkingHoursPerWeek(grouped.Key.UserName)
                select new
                           {
                               grouped.Key.UserName,
                               DisplayName = string.Format("{0} {1}.", names[1], names[0][0]),
                               grouped.Key.ProjectName,
                               Hours = grouped.Sum(g => g.Hours),
                               WorkingHoursPerWeek = workingHoursPerWeek,
                               WorkingHoursPerDay = (float)workingHoursPerWeek / 5,
                               TotalIdeallyHours = workingDaysCount * (float)workingHoursPerWeek / 5
                           } into dph
                orderby dph.DisplayName, dph.ProjectName
                select dph;
            
            // fill developers hours info
            uint columnIndex = leftColumn + 1;
            foreach (var developer in developerProjectHours.DistinctBy(dph => dph.UserName))
            {
                uint rowIndex = topRow;
                
                // fill developer name
                Document[rowIndex++, columnIndex] = developer.DisplayName;

                // fill projects hours
                var developerProjects = developerProjectHours.Where(dph => dph.UserName == developer.UserName).ToList();
                foreach (var project in ProjectsInfo)
                    Document[rowIndex++, columnIndex] = 
                        developerProjects
                        .Where(dph => dph.ProjectName == project.ProjectName)
                        .Sum(p => p.Hours);

                // fill total ideally hours
                Document[rowIndex++, columnIndex] = Math.Round(developer.TotalIdeallyHours, 1);

                // fill summary of developers hours
                var totalWorkingHours = developerProjects.Sum(dph => dph.Hours);
                Document[rowIndex++, columnIndex] = totalWorkingHours;

                // fill non commercial hours
                var totalCommercialWorkingHours = developerProjects
                    .Where(dp => !NonCommercialProjectsNames.Contains(dp.ProjectName))
                    .Sum(p => p.Hours);
                Document[rowIndex++, columnIndex] = totalCommercialWorkingHours;

                //fill percent of commercial hours to working hours
                var percentCommercialHoursToWorkingHours = totalWorkingHours != 0
                                                               ? totalCommercialWorkingHours*100/totalWorkingHours
                                                               : 0;
                Document[rowIndex++, columnIndex] = Math.Round(percentCommercialHoursToWorkingHours, 2);
                    


                // fill percent of commercial hours to ideally hours
                var percentCommercialHoursToIdeallyHours = (decimal)developer.TotalIdeallyHours != 0
                                                               ? totalCommercialWorkingHours * 100 / (decimal)(developer.TotalIdeallyHours) 
                                                               : 0;

                Document[rowIndex, columnIndex] = Math.Round(percentCommercialHoursToIdeallyHours, 2);
                    
                columnIndex++;
            }

            // fill summary of projects hours info
            var row = topRow + 1;
            foreach (var project in ProjectsInfo)
                Document[row++, rightColumn] = project.Hours;

            // fill summary of ideally hours
            var summaryIdeallyHours = (decimal)developerProjectHours.DistinctBy(dph => dph.UserName).Sum(dph => dph.TotalIdeallyHours);
            Document[row++, rightColumn] = Math.Round(summaryIdeallyHours, 1);

            // fill summary of working hours
            var summaryWorkingHours = developerProjectHours.Sum(dph => dph.Hours);
            Document[row++, rightColumn] = summaryWorkingHours;

            // fill summary of commercial hours
            var summaryCommercialWorkingHours = 
                developerProjectHours
                .Where(dph => !NonCommercialProjectsNames.Contains(dph.ProjectName))
                .Sum(dph => dph.Hours);
            Document[row++, rightColumn] = summaryCommercialWorkingHours;
                

            //fill total percent of commercial hours to working hours
            var totalPercentCommercialHoursToWorkingHours = summaryWorkingHours != 0
                                                               ? summaryCommercialWorkingHours * 100 / summaryWorkingHours
                                                               : 0;
            Document[row++, rightColumn] = Math.Round(totalPercentCommercialHoursToWorkingHours, 2);

            // fill percent of commercial hours to ideally hours
            var totalPercentCommercialHoursToIdeallyHours = summaryIdeallyHours != 0
                                                           ? summaryCommercialWorkingHours * 100 / summaryIdeallyHours
                                                           : 0;

            Document[row, rightColumn] = Math.Round(totalPercentCommercialHoursToIdeallyHours, 2);
        }
    }
}
